
package com.syu.loopcure.compensator.model;

import com.alibaba.fastjson.JSONObject;
import com.syu.loopcure.common.constants.LoopCoreGlobalConstant;
import com.syu.loopcure.common.utils.Md5Utils;
import com.syu.loopcure.compensator.enums.LogMsgEnum;
import com.syu.loopcure.compensator.interfaces.ICompensatorRecord;
import lombok.Data;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * ClassName: CompensatorInfoModel <br/>
 * date: 2020年8月11日 上午9:45:33 <br/>
 *
 * @author zhucun
 * @since JDK 1.8
 */
@Data
public class CompensatorInfoModel implements Serializable {

	/**
	 * 序列号
	 */
	private static final long serialVersionUID = 6057251612967136216L;

	private ICompensatorRecord compensatorRecord;
	
	/**
	 * 主键id
	 */
	private int id;
	/**
	 * md5Code,业务主键,md5(className + methodName + methodParamValues )
	 */
	private String md5Code;
	/**
	 * 全类名(即包名+类名)
	 */
	private String className;
	/**
	 * 方法名
	 */
	private String methodName;
	/**
	 * 参数值
	 */
	private String methodParamValues;
	/**
	 * 重试次数
	 */
	private Integer retryTimes;
	/**
	 * 重试开始时间
	 */
	private Date retryStartTime;
	/**
	 * 重试结束时间
	 */
	private Date retryEndTime;
	/**
	 * 最大重试次数
	 */
	private Integer maxRetryTimes;
	/**
	 * 重试时间间隔（秒）</br>
	 * 每次间隔相同，填写单一数字，譬如：60</br>
	 * 每次间隔不同，填写多个数字且使用逗号连接，譬如：60,300
	 */
	private String waitSeconds;
	/**
	 * 1：失败待补偿；2：取消补偿；3：补偿成功；4：重试次数达上限不再补偿
	 */
	private String status;
	/**
	 * 错误信息（失败时入库信息）
	 */
	private String failMsg;
	/**
	 * 成功信息（补偿成功记录补偿结果）
	 */
	private String successMsg;
	/**
	 * 创建时间
	 */
	private Date createTime;
	/**
	 * 更新时间
	 */
	private Date modifyTime;
	
	/**
	 * 
	 * attributeAssignment:属性赋值. <br/>  
	 *  
	 * @param className 类名
	 * @param methodName 方法名
	 * @param failMsg 失败信息
	 * @param methodParamValues 参数值
	 * @since JDK 1.8  
	 * @author zhucun  
	 * @date 2020年8月12日 上午11:33:01
	 * @modifier zhucun
	 * @modifyDate 2020年8月12日 上午11:33:01
	 */
	private void attributeAssignment(String className, String methodName, String failMsg, Object... methodParamValues) {
		Assert.notNull(className, LogMsgEnum.toLogStr(LogMsgEnum.NULL_MSG.getCode(), "className"));
		Assert.notNull(methodName, LogMsgEnum.toLogStr(LogMsgEnum.NULL_MSG.getCode(), "methodName"));
		Assert.notNull(failMsg, LogMsgEnum.toLogStr(LogMsgEnum.NULL_MSG.getCode(), "failMsg"));

		this.className = className;
		this.methodName = methodName;
		this.failMsg = failMsg;
		
		// 转换参数类型&参数值数组
		if (null != methodParamValues && methodParamValues.length > 0) {
			convertParams(methodParamValues);
		}
		
		// 业务主键生成 
		// StringBuffer 线程安全
		AtomicReference<StringBuffer> strBuffer = new AtomicReference<>(new StringBuffer(className).append(methodName).append(this.methodParamValues));
		this.md5Code = Md5Utils.md5Encode(strBuffer.toString(), LoopCoreGlobalConstant.UTF8);
	}
	
	public CompensatorInfoModel() {
		
	}
	
	public CompensatorInfoModel(String className, String methodName, String failMsg, Object... methodParamValues) {
		// 属性赋值
		attributeAssignment(className, methodName, failMsg, methodParamValues);
	}

	public CompensatorInfoModel(Method method, Object[] methodParamValues, String failMsg) {
		// 属性赋值
		attributeAssignment(method.getDeclaringClass().getName(), method.getName(), failMsg, methodParamValues);
		
	}

	/**
	 * 
	 * convertParams:转换参数类型&参数值数组. <br/>  
	 *  
	 * @param methodParamValues  参数值
	 * @since JDK 1.8  
	 * @author zhucun  
	 * @date 2020年8月11日 上午10:44:18
	 * @modifier zhucun
	 * @modifyDate 2020年8月11日 上午10:44:18
	 */
	private void convertParams(Object[] methodParamValues) {

		try {
			// 通过反射获取方法
			Class<?> clazz = Class.forName(this.className);
			Method[] allMethods = clazz.getDeclaredMethods();
			Method callableMethod = null;
			for (Method method : allMethods) {
				if (method.getName().equals(this.methodName)) {
					callableMethod = method;
					break;
				}
			}
			// 方法是否存在
			if(null == callableMethod) {
				throw new RuntimeException(LogMsgEnum.toLogStr(LogMsgEnum.METHOD_NOT_FIND.getCode(), this.methodName));
			}
			// 获取方法参数类型和数值
			// 参数类型
			Class<?>[] paramTypes = callableMethod.getParameterTypes();
			List<String> paramValueList = new LinkedList<>();
			for (int i = 0; i < methodParamValues.length; i++) {
				Object paramObj = methodParamValues[i];
				// null时设置成字符串“null”
				if(null == paramObj) {
					paramValueList.add(LoopCoreGlobalConstant.NULL_STR);
					continue;
				}
				// 字符串对象时
				if(String.class == paramTypes[i]) {
					paramValueList.add(paramObj.toString());
					continue;
				}
				// 非字符串对象时
				paramValueList.add(JSONObject.toJSONString(paramObj));
			}
			
			// 参数值数组
			this.methodParamValues = JSONObject.toJSONString(paramValueList);
		} catch (ClassNotFoundException | SecurityException e) {
			e.printStackTrace();  
			throw new RuntimeException(e);
		}

	}

}
